package com.roadmap.client.module.task;

import java.util.Date;
import java.util.Vector;

import org.apache.log4j.Logger;

import com.roadmap.client.module.BaseManager;
import com.roadmap.common.BaseConst;
import com.roadmap.common.BaseEntity;
import com.roadmap.common.RdcLogger;
import com.roadmap.common.BaseEnum.ActionType;
import com.roadmap.entities.Execution;
import com.roadmap.entities.LifeGoal;
import com.roadmap.entities.LifePlan;
import com.roadmap.entities.LifeTask;
import com.roadmap.exception.*;

public class ExecutionManager extends BaseManager {
	private final static Logger logger = RdcLogger
			.getLogger(ExecutionManager.class);

	public ExecutionManager(String aspirantId, LifeTask task) {
		super(aspirantId, Execution.class, task);
	}

	public ExecutionManager(String aspirantId, String taskId) {
		super(aspirantId, Execution.class, LifeTask.class, taskId);
	}

	public void create(BaseEntity entity) throws NullObjectException,
			InvalidActionException, InvalidValueException, ServerException,
			EntityNotFoundException, ActionNotSupportedException,
			InvalidStatusException {
		if (!checkEntityIsValid(entity)) {
			return;
		}

		Execution exec = (Execution) entity;
		if (logger.isDebugEnabled()) {
			logger.debug("execute date is: " + exec.getExecuteDate());
		}

		LifeTask task = (LifeTask) getParent();

		// only when task's status is SCHEDULED or UNDERGOING, new execution
		// can be added
		if (task.getStatus() != BaseConst.LIFETASK_STATUS_SCHEDULED
				&& task.getStatus() != BaseConst.LIFETASK_STATUS_UNDERGOING) {
			String msg = "the task (" + task.getId()
					+ ")'s status doesn't support current action.";
			ActionNotSupportedException ex = new ActionNotSupportedException();
			logger.error(msg, ex);
			throw ex;
		}

		// update the entities in one transaction
		Vector<BaseEntity> vec = new Vector<BaseEntity>();
		vec.add(exec);
		Date date = exec.getUpdatedTime();
		Date execDate = exec.getExecuteDate();

		/*
		 * if task's status is SCHEDULED, it means the execution is the first
		 * one of the task.
		 */
		if (task.getStatus() == BaseConst.LIFETASK_STATUS_SCHEDULED) {
			task.setUpdatedTime(date);
			task.setStatus(BaseConst.LIFETASK_STATUS_UNDERGOING);
			task.setRealStartDate(execDate);
			vec.add(task);

			// update plan if the task belongs to a plan
			if (task.getGoalId() != null) {
				LifePlan plan = (LifePlan) getDbUtil().getEntityById(
						LifePlan.class, task.getGoalId());
				if (plan != null) {
					if (plan.getRealStartDate() == null) {
						plan.setRealStartDate(execDate);
						plan.setUpdatedTime(date);
						vec.add(plan);
					}

					LifeGoal goal = (LifeGoal) getDbUtil().getEntityById(
							LifeGoal.class, plan.getGoalId());
					if (checkGoalBeforeCreatingExecution(goal)) {
						goal.setUpdatedTime(date);
						goal.setStatus(BaseConst.LIFEGOAL_STATUS_UNDERGOING);
						vec.add(goal);
					}
				}
			}
		}

		// update the status of entities in database
		getDbUtil().saveOrUpdateEntities(vec);
		getNoticeManager().notifyForEntry(entity, ActionType.CREATE, null);
	}

	/**
	 * check if it's necessary to update the goal
	 */
	private boolean checkGoalBeforeCreatingExecution(LifeGoal goal)
			throws EntityNotFoundException, InvalidStatusException {
		checkIfGoalIsValid(goal);
		return (goal.getStatus() == BaseConst.LIFEGOAL_STATUS_SCHEDULED) ? true
				: false;
	}

	/** if the goal is invalid, exception will be thrown */
	private void checkIfGoalIsValid(LifeGoal goal)
			throws EntityNotFoundException, InvalidStatusException {
		if (goal == null) {
			String msg = "no goal was found";
			EntityNotFoundException enfe = new EntityNotFoundException();
			logger.error(msg, enfe);
			throw enfe;
		}
		else if (goal.getStatus() != BaseConst.LIFEGOAL_STATUS_UNDERGOING
				&& goal.getStatus() != BaseConst.LIFEGOAL_STATUS_SCHEDULED) {
			/*
			 * when the task's status is SCHEDULED or UNDERGOING, the goal's
			 * status should be the same, or else it is invalid and an exception
			 * will be thrown
			 */
			String msg = "the goal ("
					+ goal.getId()
					+ ")'s status is invalid. It should be undergoing or scheduled.";
			InvalidStatusException ise = new InvalidStatusException(msg);
			logger.error(ise);
			throw ise;
		}
	}

}
